
//
// Created by ttao on 2022/7/25.
//

#include <stddef.h>
#include <limits.h>
#include <string.h>
#include <assert.h>
#include "hash/dts.h"

static inline size_t _hdrsize(char type){
    switch (type&DTS_FLAG_MASK) {
        case DTS_FLAG_8:
            return sizeof(struct dts8);
        case DTS_FLAG_16:
            return sizeof(struct dts16);
        case DTS_FLAG_32:
            return sizeof(struct dts32);
        case DTS_FLAG_64:
            return sizeof(struct dts64);
    }
    return 0;
}

/*在64位系统中LONG_MAX 与 LLONG_MAX都占8个字节*/
/*在32位系统中LONG_MAX占4个字节，LLONG_MAX占8个字节*/
static inline char _reqtype(size_t stringlen)
//判断一个字符长度适合哪种数据格式
{
    if(1<<8 > stringlen)
        return DTS_FLAG_8;
    if(1<<16 > stringlen)
        return DTS_FLAG_16;
#if (LONG_MAX == LLONG_MAX)
    if(1ll<<32 > stringlen)
        return DTS_FLAG_32;
    return DTS_FLAG_64;
#else
    return DTS_FLAG_32;
#endif
}

static inline size_t _maxlen(char type)
{
    switch (type&DTS_FLAG_MASK) {
        case DTS_FLAG_8:
            return (1 << 8) - 1;
        case DTS_FLAG_16:
            return (1 << 16) - 1;
#if (LONG_MAX == LLONG_MAX)
        case DTS_FLAG_32:
            return (1ll << 32) - 1;
#else
            return -1;
#endif
    }
}

static inline size_t hdrsize(char type)
{
    return _hdrsize(type);
}

static inline char reqtype(size_t stringlen)
{
    return _reqtype(stringlen);
}

static inline size_t maxlen(char type)
{
    return _maxlen(type);
}

dts _dtsnewlen(const char *init, size_t initlen, int trymalloc)
{
    void *sh;
    dts d;
    char type= reqtype(initlen);
    int hdrlen = hdrsize(type);
    unsigned char *fp;      /*指向数据类型*/
    size_t usable;
    assert(initlen + hdrlen + 1 > initlen);
    sh = trymalloc?                             /*sh 指向数据体*/
            d_trymalloc_usable(hdrlen+initlen+1, &usable):
            d_malloc_usable(hdrlen+initlen+1, &usable);
    if(sh == NULL) return NULL;
    memset(sh, 0, hdrlen+initlen+1);
    d = (char *) sh + hdrlen;
    fp = ((unsigned char*)d) - 1;
    usable = usable - hdrlen - 1;
    if(usable > maxlen(type))
        usable = maxlen(type);
    switch (type) {
        case DTS_FLAG_8:
        {
            DTS_VAR(8, d);
            dt->len = initlen;
            dt->alloc = usable;
            *fp = type;
            break;
        }
        case DTS_FLAG_16:
        {
            DTS_VAR(16, d);
            dt->len = initlen;
            dt->alloc = usable;
            *fp = type;
            break;
        }
        case DTS_FLAG_32:
        {
            DTS_VAR(32, d);
            dt->len = initlen;
            dt->alloc = usable;
            *fp = type;
            break;
        }
        case DTS_FLAG_64:
        {
            DTS_VAR(64, d);
            dt->len = initlen;
            dt->alloc = usable;
            *fp = type;
            break;
        }
    }
    if(initlen && init)
        memcpy(d, init, initlen);
    d[initlen] = '\0';
    return d;
}
static inline dts _dtsresizelen(char* ptr, const size_t size)
{
    char newtype, oldtype = ptr[-1] & DTS_FLAG_MASK;
    int newhdrlen, oldhdrlen = hdrsize(oldtype);
    dts newsh,oldsh = ptr - oldhdrlen;
    size_t oldlen = dtslen(ptr);
    if(dtsalloc(ptr) == size) return ptr;   // 已经满足
    if(oldlen > size) oldlen = size;
    newtype = reqtype(size);
    newhdrlen = hdrsize(newtype);

    if(oldtype == newtype || (newtype < oldtype && newtype > DTS_FLAG_8))
    {
        newsh = d_realloc(oldsh, oldhdrlen + size + 1);
        if(newsh == NULL) return NULL;
        ptr = newsh + newhdrlen;
        ptr[-1] = newtype;
    } else {
        newsh = zmalloc(size);
        if(newsh == NULL) return NULL;
        memcpy((char*)newsh + newhdrlen, ptr, oldlen);
        d_free(ptr-oldhdrlen);
        ptr = (char*) newsh + newhdrlen;
        ptr[-1] = newtype;
    }
    ptr[oldlen] = '\0';
    setdtslen(ptr, oldlen);
    setdtsalloc(ptr, size);
    return ptr;
}
static inline dts _dtsmakesizefor(dts d, size_t addlen, int greedy)
// 扩展dts的字符串长度
{
    void *sh,*newsh;
    size_t avail = dtsavail(d);
    size_t len ,newlen, reqlen;
    char type, oldtype = d[-1] & DTS_FLAG_MASK;
    int hdrlen;
    size_t usable;

    if(avail >= addlen) return 0;   // 有足够的空间时退出

    len = dtslen(d);
    sh = (char *)d-hdrsize(oldtype);
    reqlen = newlen = (len+addlen);
    assert(newlen > len);
    if(greedy == 1){
        if(newlen < DTS_MAX_PREALLOC)
            newlen *= 2;
        else
            newlen += DTS_MAX_PREALLOC;
    }

    type = reqtype(newlen);

    hdrlen = hdrsize(type);
    assert(hdrlen + newlen + 1 >  reqlen);      //防止数据溢出
    if(oldtype==type){
        newsh = d_realloc_usable(sh, hdrlen+newlen+1, &usable);
        if(newlen == 0) return NULL;
        d = (char*)newsh+hdrlen;
    } else {
        newsh = d_malloc_usable(hdrlen+newlen+1, &usable);
        if(newsh == NULL) return NULL;
        memcpy((char*)newsh+hdrlen, d, len+1);
        d_free(sh);
        d = (char *)newsh + hdrlen;
        d[-1] = type;
        setdtslen(d, len);
    }
    usable = usable-hdrlen-1;
    if(usable > maxlen(type))
        usable = maxlen(type);
    setdtsalloc(d, usable);
    return d;
}
dts dtsmakesizeforgreedy(dts d, size_t addlen) {
    return _dtsmakesizefor(d, addlen, 1);
}

dts dtsmakesizefornongreedy(dts d, size_t addlen) {
    return _dtsmakesizefor(d, addlen, 0);
}
static inline dts dtsresizelen(dts ptr, const size_t size)
{
    return _dtsresizelen(ptr, size);
}

dts dtsnewlen(const char *init, size_t initlen)
{
    return _dtsnewlen(init, initlen, 0);
}

dts dtstrynewlen(const char *init, size_t initlen)
{
    return _dtsnewlen(init, initlen, 1);
}

dts dtsnew(const char * init)
{
    size_t initlen = (init == NULL)? 0 : strlen(init);
    return dtsnewlen(init, initlen);
}

dts dtsresize(dts ptr, const size_t size)
{
    if(!ptr) return NULL;
    if(size <= 0)
    {
        dtsfree(ptr);
        return NULL;
    }
    return dtsresizelen(ptr, size);
}
dts sdscpylen(dts d, const char *t, size_t len) {
    if (dtsalloc(d) < len) {
        d = dtsmakesizeforgreedy(d,len-dtslen(d));
        if (d == NULL) return NULL;
    }
    memcpy(d, t, len);
    d[len] = '\0';
    setdtslen(d, len);
    return d;
}
dts dtscpystr(dts d, const char *t){
    return sdscpylen(d, t, strlen(t));
}

dts dtscpy(dts desc, dts sre){
    return dtscpystr(desc, sre);
}

size_t dtsfree(dts d)
{
    if(d == NULL) return -1;
    return d_free((void *)(d) - hdrsize(d[-1]));
}
